﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HtmlAgilityPack;
using log4net;
using Snap.Model;

namespace SnapUtilities
{
    public static class Utilities
    {
        private static readonly ILog Logger =
            LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        /// <summary>
        /// Pulls the news info. Earliest date is greater than OR equal. Latest date is strictly less than.
        /// </summary>
        /// <param name="earliestDate">The earliest date </param>
        /// <param name="latestDate">The latest date</param>
        /// <returns></returns>
        public static Dictionary<int, string> PullNewsInfo(DateTime earliestDate, DateTime latestDate)
        {
            Logger.DebugFormat("Get all text from news items between {0} (inclusive) and {1} (exclusive).", earliestDate, latestDate);
            var context = new SnapModelContainer();
            var firstNewsItemPull = context.NewsItems
                .Where( ni => ni.PublicationDate >= earliestDate && ni.PublicationDate < latestDate )
                .ToList();
            Logger.Debug( "Filtering down news further to only those that occurred during NYSE hours" );
            var secondNewsItemPull = firstNewsItemPull
                .Where( ni => IsDuringNyseHours( TimeZone.CurrentTimeZone.ToLocalTime( ni.PublicationDate ) ) )
                .ToList();

            
            var idToTextMap = new Dictionary<int, string>();
            foreach (var newsItem in secondNewsItemPull)
            {
                idToTextMap[newsItem.Id] = CleanHtmlFromNews( newsItem.Body );
            }

            if (idToTextMap.Values.Any(String.IsNullOrWhiteSpace))
            {
                var idsOfBlankBodies =
                    idToTextMap.Where(kvp => String.IsNullOrWhiteSpace(kvp.Value)).Select(kvp => kvp.Key).ToList();
                Logger.ErrorFormat("We have {0} news articles that have no body.", idsOfBlankBodies.Count);
                foreach (var id in idsOfBlankBodies)
                {
                    Logger.ErrorFormat("  NewsId: {0}", id);
                }
            }
            Logger.DebugFormat("Completed parsing of {0} news items.", idToTextMap.Keys.Count);

            return idToTextMap;
        }

        private static bool IsDuringNyseHours(DateTime cdt)
        {
            if (cdt.DayOfWeek == DayOfWeek.Saturday || cdt.DayOfWeek == DayOfWeek.Saturday)
            {
                Logger.DebugFormat("News event occurred on {0}. Don't use news from weekends", cdt.DayOfWeek);
                return false;
            }
            var marketOpenCDT = new DateTime(cdt.Year, cdt.Month, cdt.Day, 8, 30, 0);
            var marketCloseCDT = new DateTime(cdt.Year, cdt.Month, cdt.Day, 15, 30, 0);
            if (cdt >= marketOpenCDT && cdt <= marketCloseCDT)
            {
                Logger.DebugFormat("News event occurred inside market hours: {0:HH:mm:ss}", cdt);
                return true;
            }

            return false;
        }

        public static Dictionary<int, string> PullSingleCompanyNewsInfo(DateTime earliestDate, DateTime latestDate)
        {
            var allNewsInfos = PullNewsInfo( earliestDate, latestDate );
            var context = new SnapModelContainer();
            var singleCompanyNewsIds =
                context.NewsToCompanies.Where( ntc => allNewsInfos.Keys.Contains( ntc.NewsItemId ) )
                .Select(ntc => ntc.NewsItemId )
                .Distinct()
                .ToList();

            return singleCompanyNewsIds.ToDictionary( singleCompanyNewsId => singleCompanyNewsId, singleCompanyNewsId => allNewsInfos[singleCompanyNewsId] );
        }

        public static string CleanHtmlFromNews( string rawNewsBody )
        {
            string newsBody = rawNewsBody
                    .Replace("\n", String.Empty)
                    .Replace("\t", String.Empty)
                    .Replace("\"", "'");
            var currentBody = new StringBuilder();

            var doc = new HtmlDocument();
            doc.LoadHtml(newsBody);

            foreach (var node in doc.DocumentNode.SelectNodes("//p/text()"))
            {
                currentBody.AppendLine(node.InnerHtml);
            }

            return currentBody.ToString();
        }
    }
}
